package lab.poc.erl;

import static lab.poc.erl.Objects.hash;
import static lab.poc.erl.Objects.hashCombine;

public final class Reference implements Comparable<Reference> {

	private final String nodeName;
	private final int[] refIds;
	private final int creation;
	private int hCode_ = 0;

	Reference(final String nodeName, final int[] refIds, final int creation) {
		this.nodeName = nodeName;
		this.refIds = refIds.clone();
		this.creation = creation & 0x03; // 2 bits
	}

	public final String node() {
		return nodeName;
	}

	@Override
	public int compareTo(final Reference otherRef) {
		if (otherRef == null) {
			return -1;
		}
		if (creation == otherRef.creation) {
			if (refIds[3] == otherRef.refIds[3]) {
				if (refIds[2] == otherRef.refIds[2]) {
					if (refIds[1] == otherRef.refIds[1]) {
						if (refIds[0] == otherRef.refIds[0]) {
							return nodeName.compareTo(otherRef.nodeName);
						} else {
							return refIds[0] - otherRef.refIds[0];
						}
					} else {
						return refIds[1] - otherRef.refIds[1];
					}
				} else {
					return refIds[2] - otherRef.refIds[2];
				}
			} else {
				return refIds[3] - otherRef.refIds[3];
			}
		} else {
			return creation - otherRef.creation;
		}
	}

	/**
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(final Object other) {
		if (other instanceof Reference) {
			final Reference ref = (Reference) other;
			return (nodeName.compareTo(ref.nodeName) == 0
					&& refIds[0] == ref.refIds[0] && refIds[1] == ref.refIds[1]
					&& refIds[2] == ref.refIds[2] && refIds[3] == ref.refIds[3] && creation == ref.creation);
		}
		return false;
	}

	/**
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		if (hCode_ == 0) {
			int hash = hashCombine(31, creation);
			hash = hashCombine(hash, refIds[3]);
			hash = hashCombine(hash, refIds[2]);
			hash = hashCombine(hash, refIds[1]);
			hash = hashCombine(hash, refIds[0]);
			hCode_ = hashCombine(hash, hash(nodeName));			
		}
		return hCode_;
	}

	/**
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "#Ref<" + nodeName + "." + refIds[0] + "." + refIds[1] + "."
				+ refIds[2] + "." + refIds[3] + ">";
	}
}
